﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Resources;
using System.Reflection;
using System.Xml;
using System.Xml.Serialization;
using System.Text;

using Microsoft.BizTalk.Bam.EventObservation;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Streaming;
using Microsoft.BizTalk.XPath;

using Software41.Tracking.BAM;
using Software41.Tracking.BAM.Adapters;

namespace Software41.TrackingExample.PipelineComponents
{
    [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
    [System.Runtime.InteropServices.Guid("D152E6B1-5FB7-4c94-8CAF-55C67C2C5524")]
    [ComponentCategory(CategoryTypes.CATID_Any)]  
    public partial class StartProcessTraceInfo:
                           IBaseComponent,
                            IComponentUI,
                            Microsoft.BizTalk.Component.Interop.IComponent,
                            IPersistPropertyBag
    {
        #region Private Properties
        private string _processId = String.Empty;
        
        private ResourceManager resourceManager =
            new ResourceManager("Software41.TrackingExample.PipelineComponents.StartProcessTraceInfo", Assembly.GetExecutingAssembly());
        #endregion

        #region PublicProperties
        private bool _startProcess = false;
        public bool StartProcess
        {
            get { return _startProcess; }
            set { _startProcess = value; }
        }

        private string _processName = String.Empty;
        public string ProcessName
        {
            get { return _processName; }
            set { _processName = value; }
        }
           
        private bool _startStep = false;
        public bool StartStep
        {
            get { return _startStep; }
            set { _startStep = value; }
        }

        private string _stepName = String.Empty;
        public string StepName
        {
            get { return _stepName; }
            set { _stepName = value; }
        }

        #endregion

        #region IBaseComponent
        // <summary>
        /// Name of the component
        /// </summary>
        [Browsable(false)]
        public string Name
        {
            get{return resourceManager.GetString("COMPONENTNAME", System.Globalization.CultureInfo.InvariantCulture);}
        }

         /// <summary>
         /// Version of the component
         /// </summary>
         [Browsable(false)]
         public string Version
         {
            get{return resourceManager.GetString("COMPONENTVERSION", System.Globalization.CultureInfo.InvariantCulture);}
         }
         /// <summary>
         /// Description of the component
         /// </summary>
         [Browsable(false)]
         public string Description
         {  
             get 
             {
                 return this.resourceManager.GetString("COMPONENTDESCRIPTION", System.Globalization.CultureInfo.InvariantCulture);
             }
        }
        #endregion

         #region IComponentUI
         [Browsable(false)]
         public IntPtr Icon
         {
             get
             {
                 return ((System.Drawing.Bitmap)(this.resourceManager.GetObject("COMPONENTICON", System.Globalization.CultureInfo.InvariantCulture))).GetHicon();
             }
         }


         public System.Collections.IEnumerator Validate(object projectSystem)
         {
             return null;
         }
         #endregion

        #region IComponent
        public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
        {
            try
            {
                string contextValue = String.Empty;
                string contextNamespace = "http://Software41.TrackingExample.CtxProps";
                string contextName = String.Empty;

                //don't do anything if the user doesn't want us to
                if (this._startProcess)
                {
                    var processId = Guid.NewGuid().ToString();
                    var stepId = Guid.NewGuid().ToString();

                    var contextStream = pContext.GetEventStream();
                    IEventStream eventStream = new EventStreamAdapter(contextStream);

                    //get our message body to write to the trace
                    IBaseMessagePart bodyPart = pInMsg.BodyPart;
                    Stream inboundStream = bodyPart.GetOriginalDataStream();
                    VirtualStream virtualStream = new VirtualStream();
                    ReadOnlySeekableStream readOnlySeekableStream = new ReadOnlySeekableStream(inboundStream, virtualStream);
                    XmlTextReader xmlTextReader = new XmlTextReader(readOnlySeekableStream);

                    //begin the process with the passed in name
                    ProcessTraceInfo process = new ProcessTraceInfo(this._processName, processId, eventStream);
                    
                    //stuff the message data in here as part of payload; review with Ben optimal way
                    //to do this, and do we want to make tracking this a configurable component property
                    string messageData = String.Empty;
                    readOnlySeekableStream.Position = 0;
                    XmlTextReader reader = new XmlTextReader(readOnlySeekableStream);
                    if (reader.Read())
                    {
                        messageData = reader.ReadOuterXml();
                    }

                    if (messageData.Length == 0)
                    {
                        messageData = "<Data>No MessageData Returned.</Data>";
                    }
                    
                    process.Begin(this._processName, messageData);

                    var logDetail = process.AddLogDetail();
                    logDetail.AddDetail("Beginning Process from StartProcessTraceInfo component.");

                    if (this._startStep)
                    {
                        var step = new StepInfo(this._stepName, stepId, process.ProcessInfoId, eventStream);
                        step.Begin("MessageData", messageData);
                    }

                    #region Add Context Properties
                    
                    pInMsg.Context.Write("ProcessId", contextNamespace, processId);
                    pInMsg.Context.Write("ProcessName", contextNamespace, this._processName);
                    pInMsg.Context.Write("IsProcessActive", contextNamespace, true);

                    if (this._startStep)
                    {
                        pInMsg.Context.Write("StepName", contextNamespace, this._stepName);
                        pInMsg.Context.Write("StepInfoId", contextNamespace, stepId);
                        pInMsg.Context.Write("IsStepActive", contextNamespace, true);
                    }
                    else
                    {
                        pInMsg.Context.Write("IsStepActive", contextNamespace, false);
                    }
                    #endregion

                    #region Do Work

                    #endregion

                    //stuff it back in
                    readOnlySeekableStream.Position = 0;
                    bodyPart.Data = readOnlySeekableStream;

                    if (logDetail != null)
                    {
                        logDetail.End();
                    }

                    eventStream.Flush();
                }
                else
                {
                    //Set flags for inspection by downstream message client
                    pInMsg.Context.Write("IsProcessActive", contextNamespace, false);
                    pInMsg.Context.Write("IsStepActive", contextNamespace, false);
                }
            }
            catch (Exception ex)
            {
                throw;
            }
            return pInMsg;
        }
        #endregion

        #region IPersistPropertyBag
        public void GetClassID(out Guid classID)
        {
            classID = new Guid("4E081606-7E40-48e9-8A4E-14BEE998DB4C");
        }

        public void InitNew()
        {
            throw new NotImplementedException();
        }

        public void Load(IPropertyBag propertyBag, int errorLog)
        {
            object val = this.ReadPropertyBag(propertyBag, "StartProcess");

            if (val != null)
            {
                this.StartProcess = (bool)val;
            }

            val = this.ReadPropertyBag(propertyBag, "ProcessName");

            if (val != null)
            {
                this.ProcessName = (string)val;
            }

            val = this.ReadPropertyBag(propertyBag, "StartStep");

            if (val != null)
            {
                this.StartStep = (bool)val;
            }

            val = this.ReadPropertyBag(propertyBag, "StepName");

            if (val != null)
            {
                this.StepName = (string)val;
            }
        }

        public void Save(IPropertyBag propertyBag, bool clearDirty, bool saveAllProperties)
        {
            this.WritePropertyBag(propertyBag, "StartProcess", this.StartProcess);
            this.WritePropertyBag(propertyBag, "ProcessName", this.ProcessName);
            this.WritePropertyBag(propertyBag, "StartStep", this.StartStep);
            this.WritePropertyBag(propertyBag, "StepName", this.StepName);
        }
        #endregion

        #region utility functionality
        /// <summary> 
        /// Reads property value from property bag 
        /// </summary> 
        /// <param name="pb">Property bag</param> 
        /// <param name="propName">Name of property</param> 
        /// <returns>Value of the property</returns> 
        private object ReadPropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName)
        {
            object val = null;
            try
            {
                pb.Read(propName, out val, 0);
            }
            catch (System.ArgumentException)
            {
                return val;
            }
            catch (System.Exception e)
            {
                throw new System.ApplicationException(e.Message);
            }
            return val;
        }

        /// <summary> 
        /// Writes property values into a property bag. 
        /// </summary> 
        /// <param name="pb">Property bag.</param> 
        /// <param name="propName">Name of property.</param> 
        /// <param name="val">Value of property.</param> 
        private void WritePropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName, object val)
        {
            try
            {
                pb.Write(propName, ref val);
            }
            catch (System.Exception e)
            {
                throw new System.ApplicationException(e.Message);
            }
        }
        #endregion 
    }
}
